package edu.bucknell.edashprototype;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;

import ioio.lib.api.AnalogInput;
import ioio.lib.api.PulseInput;
import ioio.lib.api.PulseInput.PulseMode;
import ioio.lib.api.TwiMaster;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.util.BaseIOIOLooper;
import ioio.lib.util.IOIOLooper;
import ioio.lib.util.android.IOIOActivity;
import android.os.Bundle;
import android.os.Environment;
import android.text.format.DateFormat;
import android.view.Menu;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
import android.widget.ToggleButton;

public class EdashPrototypeMain extends IOIOActivity{

	/**
	 * Setting up input pins
	 * 
	 * These Pins are used by the Two Wire Interface
	 * private static final int DATA_TEMP_BAT = 4;
	 * private static final int CLOCK_TEMP_BAT = 5;
	 * private static final int DATA_TEMP_AMB = 47;
	 * private static final int CLOCK_TEMP_AMB = 48;
	 */
	private static final int MAIN_VOLTAGE_PIN = 34;
	private static final int MAIN_CURRENT_PIN = 46;
	private static final int AUX_VOLTAGE_PIN = 37;
	private static final int AUX_CURRENT_PIN = 44;
	private static final int SPEED_INPUT_PIN = 11;
	private static final int TEMP_BAT_TWI_NUMBER =0;
	private static final int TEMP_AMB_TWI_NUMBER = 0;
	private static final int AMBIENT_ADDRESS = 0x4A;
	private static final int BATTERY_ADDRESS = 0x4B;
	private static final double MILLISECONDS_TO_HOURS = 1.0/(1000.0*3600);// /3600 for amp hours
	private static final String FIRST_LINE = "TIME, MAIN VOLTAGE (V), MAIN CURRENT (A), AUX VOLTAGE (V), AUX CURRENT(A), SPEED (MPH), TEMPERATURE BATTERY (F), TEMPERATURE AMBIENT (F), CHARGE REMOVED (AMP-HOURS)\n";

	/**
	 * Setting up GUI items
	 */
	private TextView console;
	private TextView mainVoltageText;
	private TextView mainCurrentText;
	private TextView auxVoltageText;
	private TextView auxCurrentText;
	private TextView speedText;
	private TextView tempBatText;
	private TextView tempAmbText;
	private TextView ampHoursText;
	private FileOutputStream outputFile;
	private String fileName;
	private ToggleButton logButton; 



	/**
	 * Setting up conversion factors for voltages
	 * 
	 */
	private static final double MAIN_VOLTAGE_FACTOR = ((237.0+4.02)/4.02);
	private static final double AUX_VOLTAGE_FACTOR = (8.06+2)/2.0;
	private static final double MAIN_CURRENT_FACTOR = (10000.0/(5.0+200.0/3.63));
	private static final double AUX_CURRENT_FACTOR = 1000/(5.0+200.0/3.63);
	private static final double SPEED_FACTOR = 3600.0/4000.0;



	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_edash_prototype_main);
		console = (TextView)findViewById(R.id.console);
		mainVoltageText = (TextView)findViewById(R.id.mainVoltage);
		mainCurrentText = (TextView)findViewById(R.id.mainCurrent);
		auxVoltageText = (TextView)findViewById(R.id.auxVoltage);
		auxCurrentText = (TextView)findViewById(R.id.auxCurrent);
		speedText = (TextView)findViewById(R.id.speed);
		tempBatText = (TextView)findViewById(R.id.batTemp);
		tempAmbText = (TextView)findViewById(R.id.ambTemp);
		ampHoursText = (TextView)findViewById(R.id.ampHours);
		logButton = (ToggleButton)findViewById(R.id.logbutton);

		//Setting up listener
		logButton.setOnCheckedChangeListener(new OnCheckedChangeListener(){
			@Override
			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
				updateConsole("Called onCheckedChange with: "+isChecked);
				if(isChecked){
					createFile();
				}else{
					try {
						outputFile.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}

			}
		});

	}

	
	/**
	 * Create a new file for logging in edash_log_yyy_MM_dd_#.csv naming convention
	 */
	private void createFile() {
		int fileCount = 0;
		File root = Environment.getExternalStorageDirectory();
		Date now = new Date();
		fileName = "edash_log_" + DateFormat.format("yyyy_MM_dd", now).toString();
		File f = new File(root, fileName+"_"+fileCount+".csv");

		while(f.exists()){
			fileCount++;
			f = new File(root,  fileName+"_"+fileCount+".csv");

		}

		updateConsole("Found nonconflicting name File");

		try {
			outputFile = new FileOutputStream(new File( root,  fileName+"_"+fileCount+".csv"), true);
			outputFile.write(FIRST_LINE.getBytes());
		} catch (FileNotFoundException e) {
			updateConsole("File not found" + fileName +fileCount);
			System.out.println(e.getStackTrace());
		} catch (IOException e) {
			updateConsole("IOException");
			e.printStackTrace();
		}
	}
	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.edash_prototype_main, menu);
		return true;
	}

	/**
	 * IOIO code.
	 * This class is used to communicate with the IOIO
	 * @author abb011
	 *
	 */
	class Looper extends BaseIOIOLooper {
		/**
		 * Setting up IOIO interfaces
		 *
		 */

		private TwiMaster batteryTemperatureInput;
		private AnalogInput mainVoltageInput;
		private AnalogInput mainCurrentInput;
		private AnalogInput auxVoltageInput;
		private AnalogInput auxCurrentInput;
		private PulseInput speedInput;

		/**
		 * Measured Values
		 */
		private double mainVoltage;
		private double mainCurrent;
		private double auxVoltage;
		private double auxCurrent;
		private double speed;
		private double batteryTemp;
		private double ambientTemp;
		private long oldTime;
		private double chargeRemoved;
		/**
		 * Called every time a connection with IOIO has been established.
		 * Typically used to open pins.
		 * 
		 * @throws ConnectionLostException
		 *             When IOIO connection is lost.
		 * 
		 * @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#setup()
		 */
		@Override
		protected void setup() throws ConnectionLostException {	
			updateConsole("Connected");
			batteryTemperatureInput = ioio_.openTwiMaster(TEMP_BAT_TWI_NUMBER, TwiMaster.Rate.RATE_100KHz, false);
			mainVoltageInput =  ioio_.openAnalogInput(MAIN_VOLTAGE_PIN);
			mainCurrentInput = ioio_.openAnalogInput(MAIN_CURRENT_PIN);
			auxVoltageInput = ioio_.openAnalogInput(AUX_VOLTAGE_PIN);
			auxCurrentInput = ioio_.openAnalogInput(AUX_CURRENT_PIN);
			speedInput = ioio_.openPulseInput(SPEED_INPUT_PIN, PulseMode.FREQ);
			oldTime = System.currentTimeMillis();
			chargeRemoved = 0;
			createFile();

		}

		@Override
		public void disconnected(){
			try {
				outputFile.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			updateConsole("Disconnected, Please Reconnect the IOIO");

		}
		/**
		 * Called repetitively while the IOIO is connected.
		 * 
		 * @throws ConnectionLostException
		 *             When IOIO connection is lost.
		 * 
		 * @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#loop()
		 */
		@Override
		public void loop() throws ConnectionLostException {
			/**
			 * Setup for TWI requests
			 * Need an empty request (Just 0) to get temperature
			 * Need 2 bytes for the response
			 */
			byte[] request = new byte[]{0};
			byte[] response = new byte[2];
			long time;
			try {
				
				mainVoltage = roundNDecimalPlaces( MAIN_VOLTAGE_FACTOR *mainVoltageInput.getVoltage(),0);
				//mainVoltage = ((int)(mainVoltage*1000))/1000.0;
				updateConsole(""+mainCurrentInput.getVoltage());
				mainCurrent =  roundNDecimalPlaces((MAIN_CURRENT_FACTOR * mainCurrentInput.getVoltage()),0);
//				mainCurrent = ((int)(mainCurrent*1000))/1000.0;


				auxVoltage = roundNDecimalPlaces(AUX_VOLTAGE_FACTOR * auxVoltageInput.getVoltage(),1);
				//auxVoltage = ((int)(auxVoltage*1000))/1000.0;
				auxCurrent =  roundNDecimalPlaces(AUX_CURRENT_FACTOR * auxCurrentInput.getVoltage(),1);
				//auxCurrent = ((int)(auxCurrent*1000))/1000.0;
				speed = roundNDecimalPlaces(SPEED_FACTOR*speedInput.getFrequency(),1);
				//speed = ((int)(speed*1000))/1000.0;
				time = System.currentTimeMillis();

				batteryTemperatureInput.writeRead(AMBIENT_ADDRESS, false, request, request.length, response,response.length);
				ambientTemp =  roundNDecimalPlaces(convertToFTemp(response),1);

				batteryTemperatureInput.writeRead(BATTERY_ADDRESS, false, request, request.length, response,response.length);
				batteryTemp = roundNDecimalPlaces(convertToFTemp(response),1);
				chargeRemoved =  roundNDecimalPlaces(chargeRemoved +calculateChargeRemoved(time-oldTime,auxCurrent),3);
				oldTime = time;

				updateGUI();
				if(logButton.isChecked()){
					updateLogFile();
				}
				// We have a race condition here but the sleep 100 should mean that we always do it in the correct order.
				Thread.sleep(100);
			} catch (InterruptedException e) {
				updateConsole("InterruptedException Caught");
			}
		}

		/**
		 * Converts the data from the PMODtmp2 to a Farenhiet Temperature
		 * @param data -reply from the PMOD
		 * @return The temperature in Farenheit
		 */
		private double convertToFTemp(byte data[]){
			double val = ((data[0]<<8)+data[1])*0.0625/8.0;
			val = val *1.8 +32.0;
			return val;
		}

		/**
		 * Update all values being displayed
		 */
		private void updateGUI(){
			runOnUiThread(new Runnable() {
				@Override
				public void run() {

					mainVoltageText.setText(""+(mainVoltage));
					mainCurrentText.setText(""+(mainCurrent));
					auxVoltageText.setText(""+(auxVoltage));
					auxCurrentText.setText(""+(auxCurrent));
					speedText.setText(""+(speed));
					tempBatText.setText(""+(batteryTemp));
					tempAmbText.setText(""+(ambientTemp)); 
					ampHoursText.setText(""+(chargeRemoved));
				}
			});

		}
		private void updateLogFile(){
			Date now = new Date();
			String time = DateFormat.format("hh:mm:ss", now).toString();
			String line = ""+time+" , %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, %.3f\n"; 
			line = String.format(line,mainVoltage, mainCurrent, auxVoltage, auxCurrent, speed, batteryTemp, ambientTemp, chargeRemoved);
			try {
				outputFile.write(line.getBytes());
			} catch (IOException e) {
				updateConsole("FILE IO ERROR");
				createFile();
				updateLogFile();
				e.printStackTrace();
			}

		}
	}




	private void updateConsole(final String line) {
		runOnUiThread(new Runnable() {
			@Override
			public void run() {
				console.setText(line);
			}
		});
	}

	/**
	 * A method to create our IOIO thread.
	 * 
	 * @see ioio.lib.util.AbstractIOIOActivity#createIOIOThread()
	 */
	@Override
	protected IOIOLooper createIOIOLooper() {
		return new Looper();
	}

	/**
	 * Calculates the amp hours used
	 * @param timeElapsed the time elapsed in miliseconds
	 * @param current the amount current flowing during this time period
	 * @return the amphours used
	 */
	private double calculateChargeRemoved(long timeElapsed, double current){
		//	updateConsole("Delta T: "+timeElapsed+ " Current: " + current);
		return current*(timeElapsed*MILLISECONDS_TO_HOURS);
	}

	private double roundNDecimalPlaces(final double value, int n){
		double factor = Math.pow(10.0, n);
		double ret = Math.round(value*factor)/factor;
		//double ret = (int)(value*factor);
		//ret /=factor;
		return ret;

	}
	//	@Override
	//	public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
	//		// if button becomes checked we don't need to do anything immidiately.
	//		updateConsole(""+logButton.isChecked());
	//		
	//	}


}
